﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;
using System.Configuration;
using System.ServiceModel.Activation;
using System.Net;
using System.IO;
using System.Xml.Linq;
using System.Web;
using WindowsLive;
namespace PC2PService
{

    class CustomHostFactory : ServiceHostFactory
    {
        protected override ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses)
        {
            CustomHost customServiceHost =
              new CustomHost(serviceType, baseAddresses[0]);
            return customServiceHost;
        }
    }

    class CustomHost : ServiceHost
    {
        public CustomHost(Type serviceType, params Uri[] baseAddresses)
            : base(serviceType, baseAddresses)
        { }
        protected override void ApplyConfiguration()
        {
            base.ApplyConfiguration();
        }
    }

    // NOTE: You can use the "Rename" command on the "Refactor" menu to change the class name "Service1" in code, svc and config file together.
    public class Service1 : IService1
    {

        string AppPath = System.Web.Hosting.HostingEnvironment.ApplicationPhysicalPath;

        string filepath = string.Empty;


        //Notification Types
        public enum NotificationType
        {
            Tile = 1,
            Toast = 2,
            Raw = 3
        }

        //Time interval required to send messages.
        public enum BatchingInterval
        {
            TileImmediately = 1,
            ToastImmediately = 2,
            RawImmediately = 3,
            TileWait450 = 11,
            ToastWait450 = 12,
            RawWait450 = 13,
            TileWait900 = 21,
            ToastWait900 = 22,
            RawWait900 = 23
        }

        private static Dictionary<Guid, Uri> _clientUris = new Dictionary<Guid, Uri>();

        public void Subscribe(Guid _uniqueID, string uri, string liveId)
        {
            //if (_clientUris.ContainsKey(_uniqueID))
            //{
            //    _clientUris[_uniqueID] = new Uri(uri);
            //}
            //else
            //{

            //    _clientUris.Add(_uniqueID, new Uri(uri));
            //}
            string channeluri = GetChannelUri(liveId);

            if (channeluri!= "")
            {
                UpdateClientUri(liveId, uri);
            }
            else
            {
                SaveWinowsLiveIds(liveId, _uniqueID, uri);

            }

        }

        //by this we will send the toast notification
        public void SendToastNotification(string title, string message, string channeluri)
        {
            
            var toastMessage = "<?xml version=\"1.0\" encoding=\"utf-8\"?>" +
                "<wp:Notification xmlns:wp=\"WPNotification\">" +
                   "<wp:Toast>" +
                      "<wp:Text1>{0}</wp:Text1>" +
                      "<wp:Text2>{1}</wp:Text2>" +
                     "</wp:Toast>" +
                "</wp:Notification>";
            toastMessage = string.Format(toastMessage, title, message);

            var messageBytes = System.Text.Encoding.UTF8.GetBytes(toastMessage);

            //foreach (var uri in _clientUris.Values)
            //{
            //    //if (channeluri != "")
            //        //SendMessage(uri, messageBytes, NotificationType.Toast);
            //}

            if (channeluri != "")
            {
                SendMessage(new Uri(channeluri), messageBytes, NotificationType.Toast);
            }
        }

        public void SendRawNotification(string message)
        {
            var messageBytes = Encoding.UTF8.GetBytes(message);

            foreach (var uri in _clientUris.Values)
            {
                SendMessage(uri, messageBytes, NotificationType.Raw);
            }
        }

        public void SendTileNotification(string imageUrl, string title, string count)
        {
            string msg = "Content-Type: text/xml\r\nX-WindowsPhone-Target: token\r\n\r\n" +
                "<?xml version=\"1.0\" encoding=\"utf-8\"?>" +
                    "<wp:Notification xmlns:wp=\"WPNotification\">" +
                         "<wp:Token>";
            if (imageUrl != null && imageUrl.Length > 0)
            {
                msg += "<wp:Img>" + imageUrl + "</wp:Img>";
            }
            msg += "<wp:Count>" + count + "</wp:Count>" +
                "<wp:Title>" + title + "</wp:Title>" +
                "</wp:Token>" +
                "</wp:Notification>";

            var messageBytes = Encoding.UTF8.GetBytes(msg);

            foreach (var uri in _clientUris.Values)
            {
                SendMessage(uri, messageBytes, NotificationType.Tile);
            }
        }

        private void SendMessage(Uri uri, byte[] messageBytes, NotificationType notificationType)
        {
            var request = (HttpWebRequest)WebRequest.Create(uri);
            request.Method = WebRequestMethods.Http.Post;
            request.ContentType = "text/xml";
            request.ContentLength = messageBytes.Length;
            request.Headers.Add("X-MessageID", Guid.NewGuid().ToString());

            if (notificationType == NotificationType.Toast)
            {
                request.Headers["X-WindowsPhone-Target"] = "toast";
                request.Headers.Add("X-NotificationClass", ((int)BatchingInterval.ToastImmediately).ToString());
            }
            else if (notificationType == NotificationType.Raw)
            {
                request.Headers.Add("X-NotificationClass", ((int)BatchingInterval.RawImmediately).ToString());
            }
            else if (notificationType == NotificationType.Tile)
            {
                request.Headers.Add("X-NotificationClass", ((int)BatchingInterval.TileImmediately).ToString());
            }

            using (var requestStream = request.GetRequestStream())
            {
                requestStream.Write(messageBytes, 0, messageBytes.Length);
            }

        }


        public string GetLastNotificationUrl(string username, out string type)
        {
            type = "other";
            //storing the current link in the xml file with xml file name as username
            filepath = Path.Combine(AppPath, "App_Data\\" + username + ".xml");
            string file = "";
            if (File.Exists(filepath))
            {
                try
                {
                    XElement Xdoc = XElement.Load(filepath);

                    int id = 1;
                    //obtain a Max id from corresponding user file
                    IEnumerable<XElement> links = (from fd in Xdoc.Elements("NotificationItem")
                                                   where fd.Element("status").Value == "1"
                                                   orderby (int)fd.Attribute("id") descending
                                                   select fd);
                    foreach (XElement ele in links)
                    {
                        id = int.Parse(ele.Attribute("id").Value);
                        file = ele.Element("message").Value;
                        type = ele.Element("type").Value;
                        break;
                    }
                }
                catch (Exception ex)
                { throw new FaultException("Could not perform operation in GetLastNotificationUrl Method"); }
                finally { }
            }
            return file;
        }


        public void SetStatus(string username)
        {
            filepath = Path.Combine(AppPath, "App_Data\\" + username + ".xml");
            if (File.Exists(filepath))
            {
                try
                {
                    XElement Xdoc = XElement.Load(filepath);

                    //obtain a Max id from corresponding user file
                    IEnumerable<XElement> links = (from fd in Xdoc.Elements("NotificationItem")
                                                   where fd.Element("status").Value == "1"
                                                   orderby (int)fd.Attribute("id") descending
                                                   select fd);
                    foreach (XElement ele in links)
                    {
                        ele.Element("status").Value = "0";
                        break;
                    }
                    Xdoc.Save(filepath);
                }
                catch (Exception ex)
                {
                    throw new FaultException("Could not perform operation in SetStatus Method");
                }
                finally
                {
                }
            }
        }

        public void SavetoFile(byte[] myStream, string message, string filename, string username, string type, string endpoint, string cid)
        {
            string channeluri = GetChannelUri(cid);
            if (channeluri != "")
            {
                string msgnotification = message;

                if (filename != "" || myStream.Length != 0)
                {
                    msgnotification = filename;
                    string saveTo = Path.Combine(AppPath, "files_" +cid+ "\\" + filename);
                    message = endpoint + username + "/" + filename;
                    //create a new Directory with the username as filename  
                    try
                    {
                        if (!Directory.Exists(Path.Combine(AppPath, username)))
                            Directory.CreateDirectory(Path.Combine(AppPath, username));

                        FileStream writeStream = new FileStream(saveTo, FileMode.Create, FileAccess.Write);
                        int read = myStream.Length;
                        writeStream.Write(myStream, 0, read);
                        writeStream.Close();
                    }
                    catch (Exception ex)
                    { throw new FaultException("Could not perform Create Directory operation in SavetoFile Method"); }
                    finally { }
                }
                //storing the current link in the xml file with xml file name as username
                filepath = Path.Combine(AppPath, "App_Data\\files_" + cid + ".xml");

                if (!File.Exists(filepath))
                {
                    //Adding new Record with root
                    try
                    {
                        XElement rootele = new XElement(username,
                            new XElement("NotificationItem",
                                new XAttribute("id", 1),
                                new XElement("message", message),
                                new XElement("date", DateTime.Now),
                                new XElement("status", "1"),
                                new XElement("type", type)));

                        //saving the element
                        rootele.Save(filepath);
                    }
                    catch (Exception ex)
                    { throw new FaultException("Could not perform Create new Element(xml file not Existed) operation in SavetoFile Method"); }
                    finally { }
                }
                else
                {
                    //if a file already exists add the xml elements to already existing elements.
                    int id = 0;


                    try
                    {
                        //Loding the existing file
                        XElement Xdoc = XElement.Load(filepath);

                        //obtain a Max id from corresponding user file
                        IEnumerable<XElement> links = (from fd in Xdoc.Elements("NotificationItem")
                                                       orderby (int)fd.Attribute("id") descending
                                                       select fd);
                        foreach (XElement ele in links)
                        {
                            id = int.Parse(ele.Attribute("id").Value);
                            break;
                        }
                        id = id + 1;

                        //creatumg a new element
                        XElement newele = new XElement("NotificationItem",
                            new XAttribute("id", id),
                            new XElement("message", message),
                            new XElement("date", DateTime.Now),
                            new XElement("status", "1"),
                            new XElement("type", type));
                        Xdoc.Add(newele);

                        //saving the element to already existing root
                        Xdoc.Save(filepath);
                    }

                    catch (Exception ex)
                    { throw new FaultException("Could not perform Create new Element(xml file already Existed) operation in SavetoFile Method"); }
                    finally { }
                }

                SendToastNotification(username, msgnotification, channeluri);
            }          
        }

        public IEnumerable<XElement> GetAllNotifications(string username)
        {

            filepath = Path.Combine(AppPath, "App_Data\\" + username + ".xml");
            IEnumerable<XElement> links = null;
            if (File.Exists(filepath))
            {
                try
                {
                    XElement Xdoc = XElement.Load(filepath);

                    //obtain a Max id from corresponding user file
                    links = (from fd in Xdoc.Elements("NotificationItem")
                             orderby (int)fd.Attribute("id") descending
                             select fd);
                }
                catch (Exception ex)
                {
                    throw new FaultException("Could not perform GetAllNotifications operation");

                }
                finally { }
            }
            return links;
        }

        public void DeleteHistory(string username)
        {
            filepath = Path.Combine(AppPath, "App_Data\\" + username + ".xml");
            if (File.Exists(filepath))
            {
                try
                {
                    if (File.Exists(Path.Combine(AppPath, username)))
                    {
                        Directory.Delete(Path.Combine(AppPath, username), true);
                    }
                    try
                    {
                        File.Delete(filepath);
                    }
                    catch (Exception ex)
                    {
                        throw new FaultException("Could not perform File Delte operation in DeleteHistory");
                    }
                }
                catch (Exception ex)
                {
                    throw new FaultException("Could not perform Directory Delte operation in DeleteHistory");
                }
                finally { }

            }
        }

        public void SaveExceptionFiletoCloud(byte[] myStream, string filename)
        {
            string saveTo = filepath = Path.Combine(AppPath, "App_Data\\" + filename);
            FileStream writeStream = new FileStream(saveTo, FileMode.Create, FileAccess.Write);
            int read = myStream.Length;
            writeStream.Write(myStream, 0, read);
            writeStream.Close();
        }

        public void SaveExceptiontoCloud(string message, string applicationName, string username)
        {

            if (!Directory.Exists(Path.Combine(AppPath, "exceptions")))
                Directory.CreateDirectory(Path.Combine(AppPath, "exceptions"));

            //if the records file does not exist 
            string saveTo = Path.Combine(AppPath, "exceptions\\" + applicationName + ".xml");
            if (!File.Exists(saveTo))
            {

                XElement rootele = new XElement("exceptions",
                               new XElement("exception",
                                   new XElement("message", message),
                                      new XElement("applicationname", applicationName),
                                      new XElement("username", username),
                                       new XElement("date", System.DateTime.Now.ToString())));
                //saving the element
                rootele.Save(saveTo);
            }
            else
            {
                XElement Xdoc = XElement.Load(saveTo);
                XElement newele = new XElement("exception",
                                    new XElement("message", message),
                                       new XElement("applicationname", applicationName),
                                        new XElement("username", username),
                                        new XElement("date", System.DateTime.Now.ToString()));
                Xdoc.Add(newele);
                Xdoc.Save(saveTo);
            }
        }

        //public void SaveLiveIds(string liveId, Guid deviceId, string ChannelUri)
        //{
        //    string saveTo = filepath = Path.Combine(AppPath, "App_Data\\Subscriptions.xml");

        //    if (!File.Exists(saveTo))
        //    {

        //        XElement rootele = new XElement("Subscriptions",
        //                       new XElement("Subscription",
        //                           new XElement("liveid", liveId),
        //                              new XElement("deviceId", deviceId),
        //                              new XElement("ChannelUri", ChannelUri),
        //                               new XElement("date", System.DateTime.Now.ToString())));
        //        //saving the element
        //        rootele.Save(saveTo);
        //    }
        //    else
        //    {
        //        XElement Xdoc = XElement.Load(saveTo);
        //        XElement newele = new XElement("Subscription",
        //                            new XElement("liveId", liveId),
        //                               new XElement("deviceId", deviceId),
        //                               new XElement("ChannelUri", ChannelUri),
        //                               new XElement("date", System.DateTime.Now.ToString()));
        //        Xdoc.Add(newele);
        //        Xdoc.Save(saveTo);
        //    }
        //}

        void SaveWinowsLiveIds(string liveId, Guid deviceId, string ChannelUri)
        {
            string saveTo = filepath = Path.Combine(AppPath, "App_Data\\Subscriptions.xml");

            if (!File.Exists(saveTo))
            {

                XElement rootele = new XElement("Subscriptions",
                               new XElement("Subscription",
                                   new XElement("liveId", liveId),
                                      new XElement("deviceId", deviceId),
                                      new XElement("ChannelUri", ChannelUri),
                                       new XElement("date", System.DateTime.Now.ToString())));
                //saving the element
                rootele.Save(saveTo);
            }
            else
            {
                XElement Xdoc = XElement.Load(saveTo);
                XElement newele = new XElement("Subscription",
                                    new XElement("liveId", liveId),
                                       new XElement("deviceId", deviceId),
                                       new XElement("ChannelUri", ChannelUri),
                                       new XElement("date", System.DateTime.Now.ToString()));
                Xdoc.Add(newele);
                Xdoc.Save(saveTo);
            }
        }

        public string GetChannelUri(string liveId)
        {
            filepath = Path.Combine(AppPath, "App_Data\\Subscriptions.xml");
            string uri = "";
            if (File.Exists(filepath))
            {
                try
                {
                    XElement Xdoc = XElement.Load(filepath);

                    int id = 1;
                    //obtain a Max id from corresponding user file
                    IEnumerable<XElement> links = (from fd in Xdoc.Elements("Subscription")
                                                   where fd.Element("liveId").Value == liveId
                                                   select fd);
                    foreach (XElement ele in links)
                    {
                        uri = ele.Element("ChannelUri").Value;
                        break;
                    }
                }
                catch (Exception ex)
                { throw new FaultException("Could not perform operation in GetLastNotificationUrl Method"); }
                finally { }
            }
            return uri;
        }


        void UpdateClientUri(string liveId,string newuri)
        {
            filepath = Path.Combine(AppPath, "App_Data\\Subscriptions.xml");
            if (File.Exists(filepath))
            {
                try
                {
                    XElement Xdoc = XElement.Load(filepath);

                    int id = 1;
                    //obtain a Max id from corresponding user file
                    IEnumerable<XElement> links = (from fd in Xdoc.Elements("Subscription")
                                                   where fd.Element("liveId").Value == liveId
                                                   select fd);
                    foreach (XElement ele in links)
                    {
                        ele.Element("ChannelUri").Value = newuri;
                        break;
                    }
                    Xdoc.Save(filepath);
                }
                catch (Exception ex)
                { throw new FaultException("Could not perform operation in GetLastNotificationUrl Method"); }
                finally { }
            }
        }
    }
}
